<template>
	<view v-if="isOpen" class="color-picker-popup">
		<view class="color-picker-popup__mask" :class="[{ show: isOpened }]" @click="close" @touchmove.stop.prevent="doNothing"></view>
		<view class="color-picker-popup__content" :class="[{ show: isOpened }]">
			<view class="color-picker-header">
				<text class="color-picker-header__title">
					颜色拾取器
				</text>
				<view class="color-picker-header__confirm" @click="onConfirm">
					<text>确定</text>
				</view>
			</view>
			<view class="color-picker-panel" :style="{backgroundColor: `rgb(${bgcolor.r},${bgcolor.g},${bgcolor.b})`}">
				<!-- #ifdef H5 -->
				<view
					class="color-picker-panel__bg drag-box"
					@tap.stop.prevent="onTouchStart($event, 0)"
					@touchstart.stop.prevent="onTouchStart($event, 0)"
					@touchmove.stop.prevent="onTouchMove($event, 0)"
					@touchend.stop.prevent="onTouchEnd($event, 0)"
				>
				<!-- #endif -->
				<!-- #ifndef H5 -->
				<view
					class="color-picker-panel__bg drag-box"
					@touchstart.stop.prevent="onTouchStart($event, 0)"
					@touchmove.stop.prevent="onTouchMove($event, 0)"
					@touchend.stop.prevent="onTouchEnd($event, 0)"
				>
				<!-- #endif -->
					<view class="color-picker-panel__mask"></view>
					<view class="color-picker-panel__pointer" :style="[pointerStyle]"></view>
				</view>
			</view>
			<view class="color-picker-control">
				<view class="color-picker-control__current">
					<view style="width: 100%; height: 100%;" :style="{ backgroundColor:`rgba(${rgba.r},${rgba.g},${rgba.b},${rgba.a})` }"></view>
				</view>
				<view class="color-picker-control__content">
					<!-- #ifdef H5 -->
					<view
						class="color-picker-control__slider drag-box"
						@tap.stop.prevent="onTouchStart($event, 1)"
						@touchstart.stop.prevent="onTouchStart($event, 1)"
						@touchmove.stop.prevent="onTouchMove($event, 1)"
						@touchend.stop.prevent="onTouchEnd($event, 1)"
					>
					<!-- #endif -->
					<!-- #ifndef H5 -->
					<view
						class="color-picker-control__slider drag-box"
						@touchstart.stop.prevent="onTouchStart($event, 1)"
						@touchmove.stop.prevent="onTouchMove($event, 1)"
						@touchend.stop.prevent="onTouchEnd($event, 1)"
					>
					<!-- #endif -->
						<view class="color-picker-control__hue"></view>
						<view class="color-picker-control__thumb" :style="{transform: `translateX(${hueSliderLeft - 10}px)`}"></view>
					</view>
					<!-- #ifdef H5 -->
					<view
						class="color-picker-control__slider drag-box"
						@tap.stop.prevent="onTouchStart($event, 2)"
						@touchstart.stop.prevent="onTouchStart($event, 2)"
						@touchmove.stop.prevent="onTouchMove($event, 2)"
						@touchend.stop.prevent="onTouchEnd($event, 2)"
					>
					<!-- #endif -->
					<!-- #ifndef H5 -->
					<view
						class="color-picker-control__slider drag-box"
						@touchstart.stop.prevent="onTouchStart($event, 2)"
						@touchmove.stop.prevent="onTouchMove($event, 2)"
						@touchend.stop.prevent="onTouchEnd($event, 2)"
					>
					<!-- #endif -->
						<view class="color-picker-control__alpha"></view>
						<view class="color-picker-control__thumb" :style="{transform: `translateX(${alphaSliderLeft - 10}px)`}"></view>
					</view>
				</view>
			</view>
			<view class="color-picker-result">
				<view class="color-picker-result__toggle" @click="toggleMode">
					<text>切换</text>
					<text>模式</text>
				</view>
				<view v-if="mode === 'hex'" class="color-picker-result__item">
					<view class="color-picker-result__value">
						<text>{{ hex }}</text>
					</view>
					<view class="color-picker-result__label">
						<text>HEX</text>
					</view>
				</view>
				<template v-else>
					<view class="color-picker-result__item">
						<view class="color-picker-result__value">
							<text>{{ rgba.r }}</text>
						</view>
						<view class="color-picker-result__label">
							<text>R</text>
						</view>
					</view>
					<view class="color-picker-result__item">
						<view class="color-picker-result__value">
							<text>{{ rgba.g }}</text>
						</view>
						<view class="color-picker-result__label">
							<text>G</text>
						</view>
					</view>
					<view class="color-picker-result__item">
						<view class="color-picker-result__value">
							<text>{{ rgba.b }}</text>
						</view>
						<view class="color-picker-result__label">
							<text>B</text>
						</view>
					</view>
					<view class="color-picker-result__item">
						<view class="color-picker-result__value">
							<text>{{ rgba.a }}</text>
						</view>
						<view class="color-picker-result__label">
							<text>A</text>
						</view>
					</view>
				</template>
			</view>
			<view v-if="swatchColors.length" class="color-picker-swatches">
				<view v-for="(item, index) in swatchColors" :key="index" class="color-picker-swatches__item">
					<view
						class="color-picker-swatches__color"
						:style="{ background:`rgba(${item.r},${item.g},${item.b},${item.a})` }"
						@click.stop="setColorBySelect(item)"
					></view>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	import { rgbToHsb, hsbToRgb, rgbToHex, parseColorToRGBA } from './color.js'
	
	export default {
		props: {
			color: {
				type: [Object, String],
				default: () => ({ r: 0, g: 0, b: 0, a: 0 })
			},
			swatches: {
				type: Array,
				default: () => []
			}
		},
		data() {
			return {
				isOpen: false,
				isOpened: false,
				
				// rgba 颜色
				rgba: {
					r: 0,
					g: 0,
					b: 0,
					a: 1
				},
				// hsb 颜色
				hsb: {
					h: 0,
					s: 0,
					b: 0
				},
				pointerLeft: 0,
				pointerTop: 0,
				hueSliderLeft: 0,
				alphaSliderLeft: 0,
				dragIndex: 0,
				bgcolor: {
					r: 255,
					g: 0,
					b: 0,
					a: 1
				},
				hex: '#000000',
				mode: 'hex', // hex rgb
			}
		},
		computed: {
			pointerStyle() {
				return {
					transform: `translate(${this.pointerLeft - 8}px, ${this.pointerTop - 8}px)`
				}
			},
			swatchColors() {
				return this.swatches.map(color => parseColorToRGBA(color))
			}
		},
		methods: {
			open() {
				return new Promise((resolve) => {
					if (this.isOpen) return
					
					this.isOpen = true
					setTimeout(() => {
						this.isOpened = true
						resolve(true)
						if (this.color) {
							this.rgba = parseColorToRGBA(this.color)
						}
						this.$nextTick(async () => {
							setTimeout(() => {
								this.getSelectorQuery()
							}, 300)
						})
					}, 50)
				})
			},
			close() {
				return new Promise((resolve) => {
					if (!this.isOpen) return
					
					this.isOpened = false
					setTimeout(() => {
						this.isOpen = false
						resolve()
					}, 300)
				})
			},
			toggleMode() {
				this.mode = this.mode === 'hex' ? 'rgb' : 'hex'
			},
			async getSelectorQuery() {
				const data = await this.getRect('.drag-box', true)
				this.position = data
				this.setColorBySelect(this.rgba)
			},
			onTouchStart(e, index) {
				const { clientX, clientY } = e.touches[0]
				this.pageX = clientX
				this.pageY = clientY
				this.setPosition(clientX, clientY, index);
			},
			onTouchMove(e, index) {
				const { clientX, clientY } = e.touches[0]
				this.moveX = clientX
				this.moveY = clientY
				this.setPosition(clientX, clientY, index)
			},
			onTouchEnd(e, index) {},
			setPosition(x, y, index) {
				this.dragIndex = index
				const { top, left, width, height } = this.position[index]
				// 设置最大最小值
				const offsetLeft = Math.max(0, Math.min(parseInt(x - left), width))
				if (index === 0) {
					this.pointerLeft = offsetLeft
					const offsetTop = Math.max(0, Math.min(parseInt(y - top), height))
					this.pointerTop = offsetTop
					this.hsb.s = parseInt((100 * offsetLeft) / width)
					this.hsb.b = parseInt(100 - (100 * offsetTop) / height)
					this.setRGB()
					this.setHEX(this.rgba)
				} else {
					if (index === 1) {
						this.hueSliderLeft = offsetLeft
					} else if (index === 2) {
						this.alphaSliderLeft = offsetLeft
					}
					this.setControl(index, offsetLeft)
				}
			},
			setRGB() {
				const rgb = hsbToRgb(this.hsb)
				this.rgba.r = rgb.r
				this.rgba.g = rgb.g
				this.rgba.b = rgb.b
			},
			setHEX(rgb) {
				this.hex = `#${(rgbToHex(rgb))}`
			},
			setControl(index, x) {
				const {
					top,
					left,
					width,
					height
				} = this.position[index]

				if (index === 1) {
					this.hsb.h = parseInt((360 * x) / width)
					this.bgcolor = hsbToRgb({
						h: this.hsb.h,
						s: 100,
						b: 100
					});
					this.setRGB()
				} else {
					this.rgba.a = +(x / width).toFixed(1)
				}
				this.setHEX(this.rgba)
			},
			setColorBySelect(getrgb) {
				const { r, g, b, a } = getrgb
				const rgb = {
					r: r ? parseInt(r) : 0,
					g: g ? parseInt(g) : 0,
					b: b ? parseInt(b) : 0,
					a: a ? a : 0
				}
				this.rgba = rgb
				this.hsb = rgbToHsb(rgb)
				this.changeViewByHsb()
			},
			changeViewByHsb() {
				const [a, b, c] = this.position;
				this.pointerLeft = parseInt(this.hsb.s * a.width / 100);
				this.pointerTop = parseInt((100 - this.hsb.b) * a.height / 100);
				this.setRGB(this.hsb.h);
				this.setHEX(this.rgba);
				this.bgcolor = hsbToRgb({
					h: this.hsb.h,
					s: 100,
					b: 100
				});
				this.hueSliderLeft = this.hsb.h / 360 * b.width;
				this.alphaSliderLeft = this.rgba.a * c.width;
			},
			doNothing() {},
			onConfirm() {
				this.$emit('confirm', {
					rgba: this.rgba,
					hex: this.hex
				})
				this.close()
			},
			getRect(selector, all) {
				return new Promise((resolve) => {
					uni.createSelectorQuery()
						.in(this)[all ? 'selectAll' : 'select'](selector)
						.boundingClientRect((rect) => {
							if (all && Array.isArray(rect) && rect.length) {
								resolve(rect)
							}
							if (!all && rect) {
								resolve(rect)
							}
						})
						.exec()
				})
			},
		}
	}
</script>

<style lang="scss" scoped>
	.color-picker {
		&-popup {
			&__mask, &__content {
				position: fixed;
				left: 0;
				right: 0;
				bottom: 0;
				z-index: 99;
				width: 100%;
				opacity: 0;
				transition-duration: 300ms;
				&.show {
					opacity: 1;
				}
			}
			&__mask {
				top: 0;
				height: 100%;
				background-color: rgba(0, 0, 0, 0.4);
			}
			&__content {
				border-radius: 16rpx 16rpx 0 0;
				background-color: #fff;
				overflow: hidden;
				transform: translateY(100%);
				&.show {
					transform: translateY(0);
				}
			}
		}
		&-header {
			position: relative;
			display: flex;
			align-items: center;
			justify-content: center;
			height: 90rpx;
			&::after {
				content: '';
				position: absolute;
				left: 0;
				right: 0;
				bottom: 0;
				height: 1px;
				background-color: #eee;
				transform-origin: bottom;
				transform: scaleY(0.5);
			}
			&__title {
				font-size: 32rpx;
				color: #333;
			}
			&__confirm {
				position: absolute;
				top: 50%;
				right: 0;
				height: 80rpx;
				padding: 0 24rpx;
				display: flex;
				align-items: center;
				font-size: 28rpx;
				color: #4A84FF;
				transform: translateY(-50%);
			}
		}
		&-panel {
			position: relative;
			margin: 24rpx;
			height: 400rpx;
			border-radius: 16rpx;
			background-color: #f00;
			overflow: hidden;
			&__bg {
				width: 100%;
				height: 100%;
				background: linear-gradient(to right,#fff,rgba(255,255,255,0));
			}
			&__mask {
				position: absolute;
				top: 0;
				left: 0;
				right: 0;
				bottom: 0;
				height: 400rpx;
				background: linear-gradient(to top,#000,rgba(0,0,0,0));
			}
			&__pointer {
				position: absolute;
				top: 0;
				left: 0;
				z-index: 2;
				width: 16px;
				height: 16px;
				border: 1px #fff solid;
				border-radius: 8px;
				transform: translate(-8px, -8px);
			}
		}
		
		&-control {
			display: flex;
			flex-direction: row;
			padding: 12rpx 24rpx;
			&__current {
				width: 100rpx;
				height: 100rpx;
				border-radius: 50rpx;
				background-color: #fff;
				background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
				background-size: 36rpx 36rpx;
				background-position: 0 0, 18rpx 18rpx;
				border: 1px #eee solid;
				overflow: hidden;
			}
			&__content {
				flex: 1;
				display: flex;
				flex-direction: column;
				justify-content: space-between;
				height: 100rpx;
				padding: 6rpx 0 6rpx 30rpx;
			}
			&__slider {
				position: relative;
				height: 16px;
				background-color: #fff;
				background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
				linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
				background-size: 32rpx 32rpx;
				background-position: 0 0, 16rpx 16rpx;
			}
			&__hue, &__alpha {
				width: 100%;
				height: 100%;
				border-radius: 8px;
			}
			&__hue {
				background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
			}
			&__alpha {
				background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0));
			}
			&__thumb {
				position: absolute;
				top: -2px;
				width: 20px;
				height: 20px;
				box-sizing: border-box;
				border-radius: 10px;
				background: #fff;
				box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1);
			}
		}
		&-result {
			display: flex;
			flex-direction: row;
			padding: 20rpx 24rpx;
			color: #999;
			text-align: center;
			&__toggle {
				display: flex;
				flex-direction: column;
				justify-content: center;
				width: 100rpx;
				height: 100rpx;
				border-radius: 10rpx;
				box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.1);
			}
			&__item {
				margin-left: 10px;
				flex: 1;
				height: 100rpx;
			}
			&__value {
				height: 50rpx;
				line-height: 50rpx;
				border-radius: 4rpx;
				font-size: 28rpx;
				border: 0.5px solid #dadbde;
			}
			&__label {
				height: 50rpx;
				line-height: 50rpx;
				font-size: 30rpx;
			}
		}
		&-swatches {
			margin: 0 -8rpx;
			padding: 0 24rpx 20rpx;
			display: flex;
			flex-direction: row;
			flex-wrap: wrap;
			&__item {
				width: 50rpx;
				height: 50rpx;
				margin: 8rpx;
				border-radius: 6rpx;
				background-color: #fff;
				background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
					linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
				background-size: 36rpx 36rpx;
				background-position: 0 0, 18rpx 18rpx;
				border: 1px #eee solid;
			}
			&__color {
				width: 100%;
				height: 100%;
				border-radius: 6rpx;
			}
		}
	}
</style>